23  物流行业数据分析

23.1 引言物流数据分析的维度

物流行业关键指标: - 时效性: 按时交货率 - 质量: 产品合格率、返修率 - 区域: 不同区域表现 - 货品: 不同货品特点

23.2 数据清洗

列表 23.1
# 注:data_wuliu.csv数据文件本地没有,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import os  # 导入操作系统接口模块
import pandas as pd  # 导入Pandas数据分析库
import numpy as np  # 导入NumPy数值计算库
import matplotlib.pyplot as plt  # 导入Matplotlib绑图库
plt.rcParams['font.sans-serif'] = 'SimHei' ## 设置中文显示

## 数据清洗部分
data = pd.read_csv('data_wuliu.csv',encoding='gbk')

#删除重复记录
data.drop_duplicates(keep='first',inplace=True) #keep='first'默认值,保留第一次出现的重复记录
#删除缺失值
data.dropna(axis=0,how='any',inplace=True) 
#删除订单行
data.drop(columns=['订单行'],inplace=True,axis=1) 
print(data.info())  # 输出数据框基本信息

#更新索引(drop=True:把原来的索引index列删除,重置index)
data.reset_index(drop=True,inplace=True)

def data_deal(number):  # 定义函数data_deal
    if number.find('万元')!= -1:#找到带有万元的,取出数字,去掉逗号,转成float,*10000
        number_new = float(number[:number.find('万元')].replace(',',''))*10000  # 替换数据中的指定值
        pass  # 占位符,暂不执行任何操作
    else: #找到带有元的,删除元,删除逗号,转成float
        number_new = float(number.replace('元','').replace(',',''))  # 替换数据中的指定值
        pass  # 占位符,暂不执行任何操作
    return number_new  # 返回计算结果
data['销售金额'] = data['销售金额'].map(data_deal)  # 对数据进行映射转换
print(data.describe())  # 输出描述性统计信息

#销售金额为0的情况,删除
data = data[data['销售金额']!=0].copy()
data['销售时间'] = pd.to_datetime(data['销售时间'])  # 转换为日期时间格式
data['交货时间'] = pd.to_datetime(data['交货时间'])  # 转换为日期时间格式
data['销售月份'] = data['销售时间'].dt.month  # 提取日期时间属性
data['交货月份'] = data['交货时间'].dt.month  # 提取日期时间属性

#1.配送是否存在问题研究
#月份维度
data['货品交货状况'] = data['货品交货状况'].str.strip() 
data1 = data.groupby(['销售月份','货品交货状况']).size().unstack() #unstack()把行索引变成列索引
data1['按时交货率'] = data1['按时交货']/(data1['按时交货']+data1['晚交货'])  # 提取按时交货列作为data1['按时交货率']变量
print("月份交货状况:\n",data1)  # 输出月份交货状况:\n

data1 = data.groupby(['销售区域','货品交货状况']).size().unstack()  # 按指定列分组聚合
data1['按时交货率'] = data1['按时交货']/(data1['按时交货']+data1['晚交货'])  # 提取按时交货列作为data1['按时交货率']变量
data1 = data1.sort_values(data1.columns[-1],ascending=False)  # 按指定列排序
print("销售区域交货状况:\n",data1)  # 输出销售区域交货状况:\n
#泰国地区的按时交货率最高,西北地区的按时交货率最低,而且非常明显,可以看出,西北地区的配送服务存在问题,需要重点关注

#货品维度
data1 = data.groupby(['货品','货品交货状况']).size().unstack()
data1['按时交货率'] = data1['按时交货']/(data1['按时交货']+data1['晚交货'])  # 提取按时交货列作为data1['按时交货率']变量
data1 = data1.sort_values(data1.columns[-1],ascending=False)  # 按指定列排序
print("不同货品交货状况:\n",data1)  # 输出不同货品交货状况:\n
#可以看出货品4的按时交货率最低

#货品和销售区域结合
data1 = data.groupby(['货品','销售区域','货品交货状况']).size().unstack()
data1['按时交货率'] = data1['按时交货']/(data1['按时交货']+data1['晚交货'])  # 提取按时交货列作为data1['按时交货率']变量
data1 = data1.sort_values(data1.columns[-1],ascending=False)  # 按指定列排序
print("货品和销售区域结合的交货状况:\n",data1)  # 输出货品和销售区域结合的交货状况:\n

#2.是否存在尚有潜力的销售区域问题研究
#月份维度
data_month = data.groupby(['销售月份','货品'])['数量'].sum().unstack()
print("月份维度货品的销售情况:\n",data_month)  # 输出月份维度货品的销售情况:\n

# 绘制各个月份的销售情况
data_month.plot(kind='line',figsize=(12,6))
plt.title('各个月份的销售情况')  # 设置图表标题
plt.savefig("1.png")  # 保存图形至文件
#货品2在10月和12月销量猛增,有可能是公司加大了营销力度,也有可能是开发了新的市场

#销售区域维度
data_region = data.groupby(['销售区域','货品'])['数量'].sum().unstack()
print("销售区域货品的销售情况:\n",data_region)  # 输出销售区域货品的销售情况:\n
#从销售区域看,每种货品销售区域为1-3个,货品1有三个销售区域,货品2有两个销售区域,货品3、4、5、6只有一个销售区域,货品2在华东地区还有较大市场空间,适合加大投入,同时货品2在西北配送时效长,用户拒收率高,从成本角度考虑,应该减少投入。

#月份和区域结合
data_month_region = data.groupby(['销售月份','销售区域','货品'])['数量'].sum().unstack()
data_month_region['货品2']  # 查看货品2在各月份和区域的销售数量

#货品2没有开发新的市场,而是在华东地区加大了营销力度 可以在7,8,9月份加大华东地区的营销力度

#3.商品是否存在质量问题研究
data['货品用户反馈'] = data['货品用户反馈'].str.strip()
data3 = data.groupby(['货品','销售区域'])['货品用户反馈'].value_counts().unstack()  # 按指定列分组聚合
data3['拒货率'] = data3['拒货']/data3.sum(axis=1)  # 计算总和
data3['合格率'] = data3['质量合格']/data3.sum(axis=1)  # 计算总和
data3['返修率'] = data3['返修']/data3.sum(axis=1)  # 计算总和
# 按合格率、返修率、拒货率降序排列以识别质量问题货品
data3.sort_values(['合格率','返修率','拒货率'],ascending=False)
#货品3 6 5 的合格率较高,返修率较低,货品1 2 4 的合格率较低,返修率较高,货品1、2、4质量存在问题,建议扩大抽检范围,增大质检力度 货品2在马来西亚的拒货率最高,按时交货率最低,猜测马来西亚对时效性要求较高,需要提高时效性。货品2在马来西亚的销售量较低,可以考虑减少投入。

23.3 数据转换

列表 23.2
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:销售金额数据的标准化转换
# =============================================================================
# 本代码处理销售金额列,将不同单位(万元、元)统一转换为元。数据转换
# 确保所有数值具有相同的量纲,便于后续分析和比较。同时还处理日期列,
# 提取时间特征(月份)用于时间序列分析。

# ==================== 定义数据转换函数 ====================
def data_deal(number):
    """
    处理销售金额,统一为元

    参数:
        number: 销售金额字符串,可能包含'万元'或'元'单位

    返回:
        float: 转换后的金额(单位:元)
    """
    # 检查字符串中是否包含'万元'
    if number.find('万元') != -1:
        # 如果包含'万元',提取数字部分并乘以10000转换为元
        number_new = float(number[:number.find('万元')].replace(',', '')) * 10000
        # number[:number.find('万元')]提取'万元'之前的数字部分
        # .replace(',', '')删除千位分隔符(如"12,345"变为"12345")
        # float()将字符串转换为浮点数
        # * 10000将万元转换为元
    else:
        # 如果不包含'万元',假设单位为元,直接转换
        number_new = float(number.replace('元', '').replace(',', ''))
        # .replace('元', '')删除'元'字符
        # .replace(',', '')删除千位分隔符
        # float()转换为浮点数
    return number_new  # 返回转换后的数值

# ==================== 应用转换函数 ====================
# 使用map函数将转换函数应用到销售金额列的每个元素
data['销售金额'] = data['销售金额'].map(data_deal)
# map()方法对Series的每个元素应用指定函数
# data_deal函数处理每个销售金额值
# 转换后,所有销售金额都是数值类型,单位为元

# ==================== 删除异常值 ====================
# 删除销售金额为0的记录(可能是退单或数据错误)
data = data[data['销售金额'] != 0].copy()
# data['销售金额'] != 0创建布尔Series,筛选金额不为0的行
# .copy()创建新副本,避免SettingWithCopyWarning警告
# 删除异常值可以提高数据质量,避免影响分析结果

# ==================== 日期转换 ====================
# 将销售时间列转换为datetime类型
data['销售时间'] = pd.to_datetime(data['销售时间'])
# pd.to_datetime将字符串转换为datetime对象
# 转换后可以使用.dt访问器提取时间特征(年、月、日、星期等)

# 将交货时间列转换为datetime类型
data['交货时间'] = pd.to_datetime(data['交货时间'])
# 同样转换为datetime类型,便于计算时间差

# ==================== 提取时间特征 ====================
# 从销售时间中提取月份(1-12)
data['销售月份'] = data['销售时间'].dt.month
# .dt.month返回月份的数值表示
# 用于分析销售的季节性规律

# 从交货时间中提取月份
data['交货月份'] = data['交货时间'].dt.month
# 同样提取交货月份,用于分析交货的月度分布

# ==================== 预览转换结果 ====================
# 打印转换后的数据前几行
print("数据转换完成:")
print(data[['销售金额', '销售时间', '交货时间']].head())
# head()默认显示前5行
# 选择这三列进行预览,确认转换成功

# ==================== 输出解读 ====================
# 数据转换使数据更适合分析:
# 1. 单位统一:所有金额都转换为元,便于比较和计算
#    例如: "5万元" → 50000, "12345元" → 12345
# 2. 日期转换:字符串 → datetime对象,可以:
#    - 计算时间差(交货周期 = 交货时间 - 销售时间)
#    - 提取时间特征(年、月、日、星期)
#    - 按时间排序和分组
# 3. 删除异常值:金额为0可能是数据错误或退单,删除可以提高分析准确性
# 4. 提取特征:销售月份和交货月份可用于:
#    - 分析季节性规律(如11月、12月销售是否增长)
#    - 对比不同月份的按时交货率
#    - 识别业务高峰期

23.4 配送问题分析

列表 23.3
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:按时交货率的多维度对比分析
# =============================================================================
# 本代码从月份和销售区域两个维度分析按时交货率,识别配送服务的薄弱环节。
# 按时交货率是物流服务的核心KPI,直接影响客户满意度。通过多维度分析,
# 可以发现系统性问题(如特定区域或时段的服务质量下降)。

# ==================== 清洗交货状态数据 ====================
# 去除货品交货状况列的前后空格
data['货品交货状况'] = data['货品交货状况'].str.strip()
# .str.strip()删除字符串开头和结尾的空白字符
# 数据录入时可能包含多余的空格,如" 按时交货 " → "按时交货"
# 清洗后可以正确分组统计

# ==================== 月份维度分析 ====================
# 按销售月份和交货状况分组,统计订单数量
data1 = data.groupby(['销售月份', '货品交货状况']).size().unstack()
# groupby按多列分组(销售月份、交货状况)
# .size()计算每个组合的数量
# .unstack()将分组索引转换为列,使数据更易读
# 结果格式:行=月份,列=交货状况('按时交货','晚交货'),值=订单数

# 计算每个月的按时交货率
data1['按时交货率'] = data1['按时交货'] / (data1['按时交货'] + data1['晚交货'])
# 按时交货率 = 按时交货订单数 / 总订单数
# 结果是0-1之间的小数,如0.95表示95%按时交货

# 打印月份维度的交货状况
print("月份交货状况:")
print(data1)  # 显示每个月的订单数和按时交货率

# ==================== 销售区域维度分析 ====================
# 按销售区域和交货状况分组,统计订单数量
data1 = data.groupby(['销售区域', '货品交货状况']).size().unstack()
# 同样的groupby操作,但分组变量改为销售区域
# 这样可以对比不同地区的服务质量

# 计算每个销售区域的按时交货率
data1['按时交货率'] = data1['按时交货'] / (data1['按时交货'] + data1['晚交货'])
# 公式相同,但现在是按区域计算

# 按按时交货率降序排序
data1 = data1.sort_values(data1.columns[-1], ascending=False)
# data1.columns[-1]是最后一列('按时交货率')
# ascending=False表示降序(按时交货率高的排在前面)
# 排序后可以快速识别表现最好和最差的区域

# 打印销售区域维度的交货状况
print("\n销售区域交货状况:")
print(data1)  # 显示每个区域的订单数和按时交货率

# ==================== 输出解读 ====================
# 多维度分析可以发现不同层面的业务问题:
#
# 1. 月份维度:
#    - 识别特定月份的服务质量下降(如双11、春节期间可能晚交货率上升)
#    - 分析季节性规律,提前准备资源
#    - 例如:12月晚交货率可能高于其他月份,因为年尾订单量大
#
# 2. 区域维度:
#    - 识别服务薄弱区域(如西北地区按时交货率最低)
#    - 优秀区域可以作为标杆,学习最佳实践
#    - 例如:华东地区按时交货率98%,西北地区仅85%,需要改进西北物流
#
# 3. 改进建议:
#    - 对按时交货率低的区域增加配送资源
#    - 在高峰期(如12月)提前做好运力储备
#    - 分析晚交货订单的共同特征,找出根本原因
#    - 建立预警机制,按时交货率低于阈值时自动报警

23.5 销售潜力分析

列表 23.4
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:货品销售的月度趋势分析
# =============================================================================
# 本代码分析不同货品在各月的销售数量,通过折线图可视化销售趋势,
# 识别热销产品和销售高峰期。销售潜力分析有助于优化库存管理和营销策略。

# ==================== 月份维度货品销售统计 ====================
# 按销售月份和货品分组,统计销售数量
data_month = data.groupby(['销售月份', '货品'])['数量'].sum().unstack()
# groupby按月份和货品两个维度分组
# ['数量']选择要聚合的列(销售数量)
# .sum()计算每组销售数量的总和
# .unstack()将货品从索引转为列,使数据更适合可视化

# 打印月份维度的货品销售情况
print("月份维度货品销售情况:")
print(data_month)  # 显示每个月各货品的销售数量

# ==================== 可视化销售趋势 ====================
# 绘制折线图,展示各货品销售数量随时间的变化
data_month.plot(kind='line', figsize=(12, 6))
# kind='line'指定绘制折线图(也可以简写为data_month.plot())
# figsize=(12, 6)设置图表尺寸为12x6英寸
# 每个货品一条线,不同颜色区分

# 设置图表标题
plt.title('各个月份的销售情况')  # 标题说明图表内容

# 设置x轴标签
plt.xlabel('月份')  # x轴表示月份(1-12)

# 设置y轴标签
plt.ylabel('销售数量')  # y轴表示销售数量

# 添加网格线,便于读取数值
plt.grid(True, alpha=0.3)  # alpha=0.3设置网格线透明度

# 添加图例
plt.legend(title='货品')  # 图例标题为"货品"

# 自动调整布局并显示图表
plt.tight_layout()  # 防止标签重叠
plt.show()  # 显示完整的折线图

# ==================== 输出解读 ====================
# 销售趋势分析可以揭示多种商业洞察:
#
# 1. 季节性规律:
#    - 某些货品在特定月份销量激增(如空调在夏季销量高)
#    - 识别规律后可以提前备货,避免缺货
#
# 2. 货品表现对比:
#    - 货品2和货品3是畅销品,销售曲线持续高于其他货品
#    - 货品4销量较低,可能需要促销或淘汰
#    - 货品1在10月和12月销量猛增,可能有促销活动
#
# 3. 业务决策支持:
#    - 库存管理:根据预测备货,减少库存积压
#    - 营销策略:在淡季推出促销,刺激销售
#    - 产品策略:淘汰滞销品,增加畅销品库存
#    - 人员安排:在高峰期增加临时工
#
# 4. 可视化优势:
#    - 折线图直观展示趋势和变化
#    - 多条线对比容易发现规律
#    - 异常点(如突然的峰值)一目了然

23.6 质量问题分析

列表 23.5
# 注:该代码块依赖的数据来自上方平台任务代码块,因其未执行,本块也无法执行

# =============================================================================
# 题目:基于用户反馈的产品质量分析
# =============================================================================
# 本代码分析货品的质量问题,包括拒货率、合格率、返修率等指标。质量
# 分析是质量管理的重要环节,可以识别问题产品和问题区域,指导改进措施。

# ==================== 清洗用户反馈数据 ====================
# 去除货品用户反馈列的前后空格
data['货品用户反馈'] = data['货品用户反馈'].str.strip()
# .str.strip()删除字符串开头和结尾的空白字符
# 用户反馈可能有"质量合格"、"拒货"、"返修"等值
# 清洗后可以正确分类统计

# ==================== 按货品和区域统计反馈 ====================
# 按货品和销售区域分组,统计各种反馈的数量
data3 = data.groupby(['货品', '销售区域'])['货品用户反馈'].value_counts().unstack()
# groupby按货品和销售区域两个维度分组
# ['货品用户反馈']是要统计的列
# .value_counts()计算每种反馈的次数
# .unstack()将反馈类型从索引转为列

# ==================== 计算各类质量指标 ====================
# 计算拒货率(拒货数量 / 总反馈数量)
data3['拒货率'] = data3['拒货'] / data3.sum(axis=1)
# data3.sum(axis=1)计算每行的总和(即该货品在该区域的总反馈数)
# 拒货率 = 拒货数 / 总数
# 拒货通常指客户拒绝收货,可能因货损、错发等原因

# 计算合格率(质量合格数量 / 总反馈数量)
data3['合格率'] = data3['质量合格'] / data3.sum(axis=1)
# 合格率 = 质合格数 / 总数
# 质量合格表示产品符合标准,客户满意

# 计算返修率(返修数量 / 总反馈数量)
data3['返修率'] = data3['返修'] / data3.sum(axis=1)
# 返修率 = 返修数 / 总数
# 返修表示产品有问题,需要维修或更换

# ==================== 排序 ====================
# 按合格率、返修率、拒货率的优先级排序
data3 = data3.sort_values(['合格率', '返修率', '拒货率'], ascending=False)
# 按多列排序,优先级从左到右
# ascending=False表示降序(合格率高的排在前面)
# 排序后可以快速识别质量最好和最差的产品-区域组合

# ==================== 输出质量分析结果 ====================
# 打印货品质量分析表
print("货品质量分析:")
print(data3)  # 显示每个货品在每个区域的质量指标

# ==================== 输出解读 ====================
# 质量分析揭示了产品和服务的多个方面:
#
# 1. 质量指标解读:
#    - 合格率:越高越好,表示产品符合客户预期
#    - 返修率:越低越好,高返修率说明产品可靠性差
#    - 拒货率:越低越好,高拒货率说明配送或产品有问题
#
# 2. 问题识别:
#    - 货品1、2、4的合格率较低,需要改进产品质量
#    - 货品2在马来西亚拒货率高,可能是国际物流问题
#    - 某些区域返修率高,可能需要检查当地配送条件
#
# 3. 改进措施:
#    - 产品:提高质量控制,加强出厂检验
#    - 包装:改进包装,减少运输损坏
#    - 配送:优化物流流程,提升服务质量
#    - 售后:建立快速响应机制,及时处理问题
#
# 4. 管理决策:
#    - 暂停质量问题产品的发货
#    - 对问题区域进行专项整改
#    - 建立质量追溯体系,找出问题根源
#    - 定期监控质量指标,持续改进

## 分析结论

**配送问题**:
- 西北地区按时交货率最低,需重点关注
- 货品4按时交货率最低

**销售潜力**:
- 货品2在华东地区有增长空间
- 10月和12月销量猛增,可能因促销活动

**质量问题**:
- 货品124合格率较低
- 货品2在马来西亚拒货率高